package com.zhima.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.j256.simplemagic.ContentInfoUtil;
import com.zhima.entity.ZhimaMinioUploadFileResponse;
import com.zhima.exception.ZhimaMinioException;
import com.zhima.properties.ZhimaMinioProperties;
import com.zhima.service.ZhimaMinioService;
import io.minio.*;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.*;

import static com.zhima.util.ZhimaMinioUtil.*;

/**
 * @author : 芝麻
 * @date : 2023-02-10 09:14
 **/
public class ZhimaMinioServiceImpl implements ZhimaMinioService {
    @Autowired
    private MinioClient minioClient;
    @Autowired
    private ZhimaMinioProperties zhimaMinioProperties;

    @Override
    public ZhimaMinioUploadFileResponse uploadFile(String bucket, String fileName, byte[] bytes) throws ZhimaMinioException {
        String fileExtension = getFileExtension(fileName);
        String contentType = ContentInfoUtil.findExtensionMatch(fileExtension).getMimeType();
        try (ByteArrayInputStream bai = new ByteArrayInputStream(bytes)) {
            String objectName = getObjectName(fileExtension, bytes);
            minioClient.putObject(
                    PutObjectArgs
                            .builder()
                            .bucket(bucket)
                            //yyyy/MM/dd/fileMd5.fileExtension
                            .object(objectName)
                            .stream(bai, bai.available(), -1)
                            .contentType(contentType)
                            .build()
            );
            objectName = "/" + objectName;
            return ZhimaMinioUploadFileResponse
                    .builder()
                    .bucket(bucket)
                    .fileMd5(objectName.substring(objectName.lastIndexOf('/') + 1, objectName.lastIndexOf('.')))
                    .fileSize(bytes.length)
                    .fileName(fileName)
                    .pathWithBucket(String.format("%s%s", bucket, objectName))
                    .pathWithOutBucket(objectName)
                    .build();
        } catch (Exception e) {
            throw new ZhimaMinioException(e, "文件上传失败");
        }
    }


    @Override
    public ZhimaMinioUploadFileResponse uploadFile(String bucket, String fileAbsolutePath) throws ZhimaMinioException {
        File file = new File(fileAbsolutePath);
        if (!file.exists()) {
            throw new ZhimaMinioException("文件不存在");
        }
        try (FileInputStream fis = new FileInputStream(file)) {
            return uploadFile(bucket, file.getName(), fis.readAllBytes());
        } catch (IOException e) {
            throw new ZhimaMinioException(e, "文件读取失败");
        }
    }

    @Override
    public ZhimaMinioUploadFileResponse mergeChunk(String bucket, String fileMd5, String chunkFileName, Integer chunkSize) throws ZhimaMinioException {
        // 根据fileMd5值拿到存放分块文件的路径
        String chunkFileFolder = getChunkFileFolder(fileMd5, zhimaMinioProperties.getChunkFolderName());
        ByteArrayInputStream[] byteArrayInputStreams = new ByteArrayInputStream[chunkSize];
        for (int chunkIndex = 0; chunkIndex < chunkSize; chunkIndex++) {
            try {
                byte[] download = download(bucket, chunkFileFolder + chunkIndex);
                byteArrayInputStreams[chunkIndex] = new ByteArrayInputStream(download);
            } catch (Exception e) {
                throw new ZhimaMinioException(e, chunkIndex + "号分块下载失败！");
            }
        }
        String fileExtension = getFileExtension(chunkFileName);
        // 现在所有的分块都在数组中了
        // 创建一个临时文件
        File tempMergeFile;
        try {
            tempMergeFile = File.createTempFile("merge", fileExtension);
        } catch (IOException e) {
            throw new ZhimaMinioException(e, "临时文件创建失败");
        }
        try (FileOutputStream fileOutputStream = new FileOutputStream(tempMergeFile)) {
            // 将数组按次序写入一个本地的临时文件
            for (ByteArrayInputStream byteArrayInputStream : byteArrayInputStreams) {
                byte[] buffer = new byte[1024];
                int temp;
                while ((temp = byteArrayInputStream.read(buffer)) != -1) {
                    fileOutputStream.write(buffer, 0, temp);
                }
            }
        } catch (IOException e) {
            throw new ZhimaMinioException("分片文件读取失败");
        }
        // 现在tempMergeFile中有着全部数据
        // 校验合并后的文件
        String mergeFileMd5 = DigestUtil.md5Hex(tempMergeFile);
        if (!mergeFileMd5.equals(fileMd5)) {
            throw new ZhimaMinioException("文件校验未通过");
        }
        // 校验通过，上传本地文件
        return uploadFile(bucket, tempMergeFile.getAbsolutePath());
    }

    @Override
    public boolean checkChunk(String bucket, String fileMd5, Integer chunkIndex) throws ZhimaMinioException {
        return existsFile(
                bucket,
                getChunkFileFolder(fileMd5, zhimaMinioProperties.getChunkFolderName()) + chunkIndex
        );
    }

    @Override
    public boolean existsFile(String bucket, String objectName) throws ZhimaMinioException {
        try {
            boolean bucketExists = minioClient.bucketExists(
                    BucketExistsArgs
                            .builder()
                            .bucket(bucket)
                            .build());
            if (!bucketExists) {
                return false;
            }
        } catch (Exception e) {
            throw new ZhimaMinioException(e, "查询桶出错");
        }
        try {
            // 如果查得到就说明存在
            GetObjectResponse response = minioClient.getObject(
                    GetObjectArgs
                            .builder()
                            .bucket(bucket)
                            .object(objectName)
                            .build()
            );
            return response != null && response.readAllBytes().length != 0;
        } catch (Exception e) {
            throw new ZhimaMinioException(e, "查询桶对象出错");
        }
    }

    @Override
    public byte[] download(String bucket, String objectName) throws ZhimaMinioException {
        try {
            return minioClient.getObject(
                    GetObjectArgs
                            .builder()
                            .bucket(bucket)
                            .object(objectName)
                            .build()
            ).readAllBytes();
        } catch (Exception e) {
            throw new ZhimaMinioException(e, "下载文件失败");
        }
    }

    @Override
    public void deleteFile(String bucket, String objectName) throws ZhimaMinioException {
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucket)
                            .object(objectName)
                            .build()
            );
        } catch (Exception e) {
            throw new ZhimaMinioException(e, "文件删除失败");
        }
    }
}
